package org.jfsm.core;

import java.lang.reflect.Field;
import java.util.ArrayList;

abstract class SignalGroup {

    static abstract class Visitor {
        abstract void visit(Field f) throws Exception;

        void visitM(Field f) throws Exception {
        }

        void visitS(Field f) throws Exception {
        }
    }

    int numSignals;

    abstract FSMSignal newSignal(Field f);

    abstract FSMSignal newSignalM(Field f);

    abstract FSMSignal newSignalS(Field f);

    void initialize() throws FSMException {
        visitSignals(true, new Visitor() {

            @Override
            public void visit(Field f) throws Exception {
                FSMSignal s = newSignal(f);
                s.setEid(numSignals++);
                f.set(SignalGroup.this, s);
            }

            @Override
            public void visitM(Field f) throws Exception {
                FSMSignal s = newSignalM(f);
                s.setEid(numSignals++);
                f.set(SignalGroup.this, s);
            }

            @Override
            public void visitS(Field f) throws Exception {
                FSMSignal s = newSignalS(f);
                if (s != null) {
                    s.setEid(numSignals++);
                    f.set(SignalGroup.this, s);
                } else {
                    throw new FSMException("SignalS is not supported in this class");
                }
            }

        });
    }

    void getNames(final String prefix, final ArrayList<String> signalNames) throws FSMException {
        visitSignals(false, new Visitor() {
            @Override
            public void visit(Field f) throws Exception {
                signalNames.add(prefix + "." + f.getName());
            }
        });
    }

    void visitSignals(boolean seperateTypes, Visitor v) throws FSMException {
        for (Field f : getClass().getDeclaredFields()) {
            try {
                if (seperateTypes && FSMSignalS.class.isAssignableFrom(f.getType())) {
                    f.setAccessible(true);
                    v.visitS(f);
                } else if (seperateTypes && FSMSignalM.class.isAssignableFrom(f.getType())) {
                    f.setAccessible(true);
                    v.visitM(f);
                } else if (FSMSignal.class.isAssignableFrom(f.getType())) {
                    f.setAccessible(true);
                    v.visit(f);
                }

            } catch (Exception e) {
                throw new FSMException(e);
            }
        }
    }

}
